package com.haiyou.common.util.string;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;

import org.springframework.util.StringUtils;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import info.debatty.java.stringsimilarity.Levenshtein;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: 字符串相似性检测过滤类
 * @author xingyuan
 * @date 2022年11月14日 下午3:52:45
 */
@Slf4j
public class KeyStringLevenshteinFilter {
	
	/**
	 * 单个key 字符串数量
	 */
	@Getter
	private int keyCacheNum;
	
	/**
	 * key 过期时间
	 */
	@Getter
	private int expireAfterAccess;
	
	/**
	 * 阈值次数
	 */
	@Getter
	private int threshold = 5;
	
	/**
	 * 缓存个数
	 */
	@Getter
	private int maximumSize;
	
	/**
	 * 相似度值
	 */
	@Getter
	private double distanceValue = 5.0;
	
	/**
	 * 缓存
	 */
	private Cache<String,StringRecords> cache;
	
	/**
	 * 忽略的大小
	 */
	private static int ignoreLenght = 5;
	
	/**
	 *  Levenshtein 算法实现	O(m*n) 1
	 */
	@Getter
	private Levenshtein distance;
	
	/**
	 * 是否检测通过
	 * @param str
	 * @return true 检测通过 false 失败
	 */
	public boolean distance(String key,String str) {
		
		if(!StringUtils.hasLength(str)) {
			return true;
		}
		
		str = str.trim();

		//小于 5个字符不需要处理
		if(str.length() <= ignoreLenght) {
			return true;
		}

		StringRecords record = cache.get(key, k->{
			return new StringRecords();
		});

		
		Iterator<StringRecord> iterator = record.getRecords().iterator();
		
		
		boolean result = true;
		
		while(iterator.hasNext()) {
			
			StringRecord entry = iterator.next();
			
			//log.info("{},{}相似度:{}",entry.getKey(),str,distance.distance(entry.getKey(),str));
			//相似度
			if(distance.distance(entry.getKey(),str) <= distanceValue) {
				
				entry.setNum(entry.getNum() + 1);
				
				int value = entry.getNum();
				
				if(value >= threshold) {
					
					entry.setNum(0);
					
					return false;
				}
			}
			
			if(entry.getKey().equals(str)) {
				result = false;
			}
			
		}
		
		if(result) {
			addRecord(record, str);
		}
		
		return true;
	}
	
	/**
	 * 添加记录
	 * @param record
	 * @param str
	 */
	private void addRecord(StringRecords record,String str) {
		
		
		//添加记录
		StringRecord stringRecord = new StringRecord();
		
		stringRecord.setNum(1);
		stringRecord.setKey(str);
		//没有相似字符串
		record.getRecords().add(stringRecord);
		
		if(record.getRecords().size() > keyCacheNum){
			record.getRecords().removeFirst();
		}		
		
	}
	
	
	
	public static void main(String[] args) {
		
		
		KeyStringLevenshteinFilter filter = KeyStringLevenshteinFilter.create();
		
		String playerId = "10001";
		
		String str = "test:{}";
		
		for (int i = 0; i < 100; i++) {
			str = str + "1" ;
			log.info(String.valueOf(filter.distance(playerId, str)));
		}
		
		
		
	}
	
	
	private KeyStringLevenshteinFilter(int expireAfterAccess,int maximumSize,
			int keyCacheNum,int threshold,double distanceValue) {
		this.keyCacheNum = keyCacheNum;
		this.expireAfterAccess = expireAfterAccess;
		this.threshold = threshold;
		this.maximumSize = maximumSize;
		this.distanceValue = distanceValue;
		distance = new Levenshtein();
		
		this.cache = Caffeine.newBuilder()
				//设置最大条目
				.maximumSize(maximumSize)
				//设置读写过去时间 单位s
				.expireAfterAccess(expireAfterAccess, TimeUnit.SECONDS)
				.build();
	}
	
	/**
	 * 设置参数
	 * @param expireAfterAccess
	 * @param keyCacheNum 缓存key个数
	 * @param threshold 相似次数阈值
	 * @param distanceValue 相似度阈值
	 * @return
	 */
	public static KeyStringLevenshteinFilter create(int expireAfterAccess,int maximumSize,int keyCacheNum,
			int threshold,double distanceValue) {
		return new KeyStringLevenshteinFilter(expireAfterAccess,maximumSize,keyCacheNum,threshold,distanceValue);
	}
	
	/**
	 * 默认 5分钟过期时间 50个字符串
	 * @return
	 */
	public static KeyStringLevenshteinFilter create() {
		return create(5*50,1000,50,5,5.0);
	}
	


}
